<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>迷宫</title>
    <link rel="icon" href="/get_image">
    
    <style>
        body {
            background-color: #a1a1a1;
            display: flex;
            justify-content: space-around;
        }

        canvas {
            display: block;
            background-color: #75988b;
        }

        .btn {
            height: 40px;
            display: block;
        }


        input {
            width: 200px;
        }
    </style>
</head>

<body>
    <div id="canvas_container"></div>

    <div style="height: 500px;width: 400px;">
        <div style="height:40px">
            <button onclick="random_generation()" style="display: inline-block;height: 40px;">随机生成围墙</button>
            <select style="display: inline-block;height: 40px;" onclick="choice_solution(value)">
                <option value="null" selected disabled>请选择一种算法</option>
                <option value="DFS">DFS</option>
                <option value="BFS">BFS</option>
                <option value="A*">A*</option>
            </select>
        </div>
        <button onclick="draw_attempt_path()" class="btn text"></button>
        <button onclick="draw_target_path()" class="btn">显示通关路径</button>
        <button onclick="xiao_cai_dan()" class="btn">小彩蛋</button>
        <div style="height: 40px;">
            <label for="range-input" class="text">

            </label>
            <input type="range" id="range-input" value="0" min="0" max="1" step="0.001"
                oninput="attempt_path_progress(value,0)" />
        </div>
        <div style="height: 40px;">
            <label for="nuber-input" class="text">

            </label>
            <input type="number" value="0" id="nuber-input" min="0" oninput="attempt_path_progress(value,1)" />
        </div>
    </div>
</body>

<script>
    const xhr = new XMLHttpRequest();
    const canvas_container = document.querySelector('#canvas_container');
    const canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d');
    canvas.width = canvas.height = window.innerHeight - 30;
    const gap_width = canvas.width / 18;  // 将棋盘分为18份，每一份的宽度为 gap_width
    canvas_container.appendChild(canvas);

    const number_input = document.querySelector('#nuber-input');
    const range_input = document.querySelector('#range-input');
    const btnArr = document.querySelectorAll('.btn');
    const textArr = document.querySelectorAll('.text');
    btnArr.forEach(btn => {
        btn.disabled = true;// 锁定
    })

    ctx.lineWidth = 0.01;
    ctx.font = `13px 宋体`;
    ctx.textAlign = "center";
    //垂直对齐方式
    ctx.textBaseline = "middle";

    // 5代表可走
    let arr2 = Array.from({ length: 17 }, () => Array(17).fill(5));// 初始化元素为5的17*17的矩阵
    let target_path = [];
    let attempt_path = [];
    let solution = null;
    // 随机生成围墙
    const random_generation = () => {
        for (let i = 0; i < 17; i++) {
            for (let j = 0; j < 17; j++) {
                if (Math.random() > 0.6) {
                    arr2[i][j] = 6;
                } else {
                    arr2[i][j] = 5;
                }
            }
        }
        arr2[0][0] = 5;
        arr2[16][16] = 5;
        draw(arr2);
    }
    const attempt_path_progress = (value, type) => {
        draw(arr2);
        if (type === 0) {// 根据比例
            for (let i = 0; i < attempt_path.length * value; i++) {
                if (i > 0) {
                    let [x, y] = attempt_path[i - 1];
                    ctx.clearRect((y + 0.5) * gap_width, (x + 0.5) * gap_width, gap_width, gap_width);
                    ctx.fillStyle = "#75988b";
                    ctx.fillRect((y + 0.5) * gap_width, (x + 0.5) * gap_width, gap_width, gap_width);// 绘制绿色底色
                    ctx.fillStyle = "rgba(0,101,255,0.3)";
                    ctx.fillRect((y + 0.5) * gap_width, (x + 0.5) * gap_width, gap_width, gap_width);
                    ctx.fillStyle = "#000000";
                    ctx.fillText(i.toString(), (y + 1) * gap_width, (x + 1) * gap_width);
                }
                let [x, y] = attempt_path[i];
                ctx.fillStyle = "#000000";
                ctx.fillText(i.toString(), (y + 1) * gap_width, (x + 1) * gap_width);
                ctx.drawImage(image, (y + 0.5) * gap_width, (x + 0.5) * gap_width, gap_width, gap_width);
                number_input.value = i + 1;  // 同步步数
            }
        }
        if (type === 1) {// 根据步数
            for (let i = 0; i < value; i++) {
                if (i > 0) {
                    let [x, y] = attempt_path[i - 1];
                    ctx.clearRect((y + 0.5) * gap_width, (x + 0.5) * gap_width, gap_width, gap_width);
                    ctx.fillStyle = "#75988b";
                    ctx.fillRect((y + 0.5) * gap_width, (x + 0.5) * gap_width, gap_width, gap_width);// 绘制绿色底色
                    ctx.fillStyle = "rgba(0,101,255,0.3)";
                    ctx.fillRect((y + 0.5) * gap_width, (x + 0.5) * gap_width, gap_width, gap_width);
                    ctx.fillStyle = "#000000";
                    ctx.fillText(i.toString(), (y + 1) * gap_width, (x + 1) * gap_width);
                }
                let [x, y] = attempt_path[i];
                ctx.fillStyle = "#000000";
                ctx.fillText(i.toString(), (y + 1) * gap_width, (x + 1) * gap_width);
                ctx.drawImage(image, (y + 0.5) * gap_width, (x + 0.5) * gap_width, gap_width, gap_width);
                range_input.value = value / attempt_path.length; // 同步比例
            }
        }
    }
    // 算法选择
    const choice_solution = value => {
        if (value !== "null") {
            textArr[0].innerText = '演示 ' + value + ' 过程';
            textArr[1].innerText = value + " 进度：";
            textArr[2].innerText = value + " 步数：";
            btnArr.forEach(btn => {
                btn.disabled = false; // 解除锁定
            })
            draw(arr2);// 清除棋盘上已有的路径
            solution = value; // 更新全局变量
        }
    }
    // 画网格线
    const draw_line = () => {
        for (let i = 1; i <= 16; i++) {
            ctx.moveTo(gap_width * (0.5 + i), gap_width * 0.5);
            ctx.lineTo(gap_width * (0.5 + i), 17.5 * gap_width);
            ctx.stroke();
            ctx.moveTo(gap_width / 2, gap_width * (0.5 + i));
            ctx.lineTo(17.5 * gap_width, gap_width * (0.5 + i));
            ctx.stroke();
        }
    }
    draw_line();

    // 黑色外围墙
    ctx.fillStyle = '#000000';
    ctx.fillRect(0, 0, gap_width * 18, gap_width / 2);
    ctx.fillRect(0, 17.5 * gap_width, gap_width * 18, gap_width / 2);
    ctx.fillRect(0, gap_width * 1.5, gap_width / 2, gap_width * 18);
    ctx.fillRect(17.5 * gap_width, 0, gap_width / 2, gap_width * 16.5);
    // 标记起点
    ctx.fillStyle = '#ffffff';
    ctx.fillText("起点", gap_width, gap_width);

    const send = () => {
        let word_arr = [];
        for (let row = 0; row < arr2.length; row++) {
            for (let col = 0; col < arr2[row].length; col++) {
                if (arr2[row][col] === 6) {
                    word_arr.push([row, col]);
                }
            }
        }
        xhr.open('POST', "/cheated", false);
        xhr.setRequestHeader('Content-Type', 'application/json');// 请求头
        xhr.send(JSON.stringify({ maze: arr2, solution: solution }));
    }
    // 请求响应后自动调用
    xhr.onreadystatechange = function () {
        if (xhr.readyState === 4 && xhr.status === 200) {
            let data = JSON.parse(xhr.responseText);// 反序列化
            if (data.target_path.length === 0) {
                alert("无解");
                attempt_path = [];
                target_path = [];
                return;
            }
            number_input.max = attempt_path.length.toString();
            attempt_path = data.attempt_path;
            target_path = data.target_path;
        }
    };


    const image = new Image();
    image.src = "/get_image";


    const xiao_cai_dan = () => {
        draw(Array.from({ length: 17 }, () => Array(17).fill(5)));
        let lai = [[1, 5], [1, 11], [2, 3], [2, 4], [2, 5], [2, 6], [2, 7], [2, 10], [2, 11], [2, 12], [2, 13], [2, 14], [3, 5], [3, 9], [3, 10], [3, 13], [4, 3], [4, 4], [4, 5], [4, 6], [4, 7], [4, 9], [4, 12], [5, 3], [5, 5], [5, 7], [5, 10], [5, 11], [5, 12], [5, 13], [5, 14], [6, 3], [6, 5], [6, 7], [6, 10], [6, 14], [7, 3], [7, 4], [7, 5], [7, 6], [7, 7], [7, 10], [7, 12], [7, 14], [8, 5], [8, 10], [8, 12], [8, 14], [9, 4], [9, 5], [9, 6], [9, 10], [9, 12], [9, 14], [10, 3], [10, 5], [10, 7], [10, 10], [10, 12], [10, 14], [11, 3], [11, 5], [11, 7], [11, 12], [12, 2], [12, 5], [12, 7], [12, 11], [12, 12], [12, 13], [13, 2], [13, 5], [13, 8], [13, 10], [13, 11], [13, 13], [13, 14], [14, 1], [14, 5], [14, 8], [14, 10], [14, 14], [15, 5], [15, 9], [15, 15]];
        draw_word(lai);
        attempt_path = [];// 清楚记录
        target_path = [];
        setTimeout(() => {
            draw(Array.from({ length: 17 }, () => Array(17).fill(5)));
            let wen = [[1, 5], [1, 6], [1, 7], [2, 6], [2, 7], [3, 7], [3, 8], [5, 2], [5, 3], [5, 4], [5, 5], [5, 6], [5, 7], [5, 8], [5, 9], [5, 10], [5, 11], [5, 12], [5, 13], [5, 14], [6, 1], [6, 2], [6, 10], [6, 11], [7, 3], [7, 4], [7, 9], [7, 10], [8, 4], [8, 5], [8, 9], [9, 5], [9, 6], [9, 8], [9, 9], [10, 6], [10, 7], [10, 8], [11, 7], [11, 8], [11, 9], [12, 6], [12, 7], [12, 9], [12, 10], [13, 5], [13, 6], [13, 10], [13, 11], [13, 12], [14, 3], [14, 4], [14, 5], [14, 11], [14, 12], [14, 13], [15, 2], [15, 3], [15, 12], [15, 13], [15, 14], [15, 15]];
            draw_word(wen);
        }, 500);
        setTimeout(() => {
            draw(Array.from({ length: 17 }, () => Array(17).fill(5)));
            let hao = [[1, 11], [2, 2], [2, 9], [2, 11], [3, 2], [3, 3], [3, 9], [3, 11], [4, 3], [4, 4], [4, 8], [4, 9], [4, 11], [5, 4], [5, 5], [5, 8], [5, 11], [6, 5], [6, 7], [6, 8], [6, 9], [6, 10], [6, 11], [6, 12], [6, 13], [6, 14], [7, 3], [7, 4], [7, 6], [7, 7], [7, 11], [8, 2], [8, 3], [8, 11], [9, 1], [9, 2], [9, 5], [9, 7], [9, 8], [9, 9], [9, 10], [9, 11], [9, 12], [9, 13], [9, 14], [9, 15], [10, 5], [11, 4], [11, 5], [11, 8], [11, 9], [11, 10], [11, 11], [11, 12], [11, 13], [11, 14], [12, 3], [12, 4], [12, 8], [12, 14], [13, 2], [13, 3], [13, 4], [13, 8], [13, 14], [14, 2], [14, 3], [14, 8], [14, 14], [15, 2], [15, 8], [15, 9], [15, 10], [15, 11], [15, 12], [15, 13], [15, 14]];
            draw_word(hao);
        }, 1000);
        //
        setTimeout(() => {
            draw(Array.from({ length: 17 }, () => Array(17).fill(5)));
            let gong = [[1, 6], [1, 10], [2, 6], [2, 10], [3, 3], [3, 4], [3, 5], [3, 6], [3, 7], [3, 8], [3, 9], [3, 10], [3, 11], [3, 12], [3, 13], [4, 6], [4, 10], [5, 6], [5, 10], [6, 2], [6, 3], [6, 4], [6, 5], [6, 6], [6, 7], [6, 8], [6, 9], [6, 10], [6, 11], [6, 12], [6, 13], [6, 14], [7, 5], [7, 6], [7, 10], [7, 11], [8, 5], [8, 11], [9, 4], [9, 5], [9, 8], [9, 11], [9, 12], [10, 3], [10, 4], [10, 8], [10, 10], [10, 12], [10, 13], [11, 2], [11, 3], [11, 5], [11, 6], [11, 8], [11, 9], [11, 11], [11, 13], [11, 14], [12, 1], [12, 2], [12, 4], [12, 5], [12, 8], [12, 9], [12, 10], [12, 12], [12, 14], [12, 15], [13, 3], [13, 4], [13, 8], [13, 10], [13, 13], [14, 6], [14, 8], [15, 7], [15, 8]];
            draw_word(gong);
        }, 1500);
        setTimeout(() => {
            draw(Array.from({ length: 17 }, () => Array(17).fill(5)));
            let xi = [[1, 8], [2, 3], [2, 4], [2, 5], [2, 6], [2, 7], [2, 8], [2, 9], [2, 10], [2, 11], [2, 12], [2, 13], [3, 8], [4, 4], [4, 5], [4, 6], [4, 7], [4, 8], [4, 9], [4, 10], [4, 11], [4, 12], [6, 4], [6, 5], [6, 6], [6, 7], [6, 8], [6, 9], [6, 10], [6, 11], [6, 12], [7, 4], [7, 12], [8, 4], [8, 5], [8, 6], [8, 7], [8, 8], [8, 9], [8, 10], [8, 11], [8, 12], [9, 3], [9, 13], [10, 4], [10, 12], [11, 1], [11, 2], [11, 3], [11, 4], [11, 5], [11, 6], [11, 7], [11, 8], [11, 9], [11, 10], [11, 11], [11, 12], [11, 13], [11, 14], [11, 15], [13, 4], [13, 5], [13, 6], [13, 7], [13, 8], [13, 9], [13, 10], [13, 11], [13, 12], [14, 4], [14, 12], [15, 4], [15, 5], [15, 6], [15, 7], [15, 8], [15, 9], [15, 10], [15, 11], [15, 12]]
            draw_word(xi);
        }, 2000);
        setTimeout(() => {
            draw(Array.from({ length: 17 }, () => Array(17).fill(5)));
            let ni = [[1, 5], [1, 6], [1, 10], [2, 4], [2, 5], [2, 9], [2, 10], [2, 11], [2, 12], [2, 13], [2, 14], [2, 15], [3, 3], [3, 4], [3, 8], [3, 9], [3, 13], [3, 14], [4, 2], [4, 3], [4, 7], [4, 8], [4, 13], [5, 1], [5, 2], [5, 4], [5, 6], [5, 7], [5, 10], [5, 12], [6, 4], [6, 10], [7, 4], [7, 10], [8, 4], [8, 8], [8, 10], [8, 12], [9, 4], [9, 7], [9, 8], [9, 10], [9, 12], [9, 13], [10, 4], [10, 7], [10, 10], [10, 13], [11, 4], [11, 6], [11, 7], [11, 10], [11, 13], [12, 4], [12, 6], [12, 10], [12, 13], [12, 14], [13, 4], [13, 8], [13, 10], [13, 14], [13, 15], [14, 4], [14, 9], [14, 10], [15, 4], [15, 10]]
            draw_word(ni);
        }, 2500);
        setTimeout(() => {
            draw(Array.from({ length: 17 }, () => Array(17).fill(5)));
            let yi = [[2, 3], [2, 4], [2, 5], [2, 6], [2, 7], [2, 8], [2, 9], [2, 10], [2, 11], [3, 11], [4, 11], [5, 3], [5, 11], [6, 3], [6, 9], [6, 10], [6, 11], [7, 3], [7, 4], [7, 5], [7, 6], [7, 7], [7, 8], [8, 3], [9, 3], [10, 3], [11, 3], [12, 3], [12, 14], [12, 15], [13, 3], [13, 4], [13, 13], [13, 14], [14, 4], [14, 5], [14, 6], [14, 7], [14, 8], [14, 9], [14, 10], [14, 11], [14, 12], [14, 13]]
            draw_word(yi);
        }, 3000);
        setTimeout(() => {
            draw(Array.from({ length: 17 }, () => Array(17).fill(5)));
            let tong = [[1, 9], [1, 10], [1, 11], [1, 12], [1, 13], [1, 14], [2, 2], [2, 3], [2, 8], [2, 9], [2, 13], [3, 3], [3, 4], [3, 7], [3, 8], [3, 12], [4, 4], [4, 10], [4, 11], [5, 11], [5, 12], [6, 2], [6, 3], [6, 4], [6, 5], [6, 8], [6, 9], [6, 10], [6, 11], [6, 12], [6, 13], [6, 14], [7, 5], [7, 8], [7, 11], [7, 14], [8, 4], [8, 5], [8, 8], [8, 10], [8, 11], [8, 12], [8, 14], [9, 3], [9, 4], [9, 8], [9, 11], [9, 14], [10, 3], [10, 8], [10, 10], [10, 11], [10, 12], [10, 14], [11, 4], [11, 8], [11, 11], [11, 14], [12, 4], [12, 8], [12, 11], [12, 14], [13, 3], [13, 4], [13, 5], [13, 13], [13, 14], [14, 1], [14, 2], [14, 5], [14, 6], [14, 7], [14, 8], [15, 9], [15, 10], [15, 11], [15, 12], [15, 13], [15, 14], [15, 15]]
            draw_word(tong);
        }, 3500);
        setTimeout(() => {
            draw(Array.from({ length: 17 }, () => Array(17).fill(5)));
            let guan = [[1, 4], [1, 5], [1, 11], [1, 12], [2, 5], [2, 6], [2, 10], [2, 11], [3, 6], [3, 7], [3, 9], [3, 10], [4, 9], [5, 4], [5, 5], [5, 6], [5, 7], [5, 8], [5, 9], [5, 10], [5, 11], [5, 12], [6, 8], [7, 8], [8, 8], [9, 7], [10, 3], [10, 4], [10, 5], [10, 6], [10, 7], [10, 8], [10, 9], [10, 10], [10, 11], [10, 12], [10, 13], [11, 6], [11, 7], [11, 9], [12, 5], [12, 6], [12, 10], [13, 4], [13, 5], [13, 11], [13, 12], [14, 3], [14, 4], [14, 12], [14, 13], [15, 2], [15, 3], [15, 13], [15, 14], [15, 15]]
            draw_word(guan);
        }, 4000);
        arr2 = Array.from({ length: 17 }, () => Array(17).fill(5));
    }
    // 落子监听事件
    canvas.addEventListener('click', e => {
        // 获取棋子在棋盘中的 像素 位置
        let x = Math.floor((e.offsetX + gap_width / 2));
        let y = Math.floor((e.offsetY + gap_width / 2));
        if (x < gap_width * 0.5 || x > 18 * gap_width || y < gap_width * 0.5 || y > 18 * gap_width) {
            return;
        }
        // 计算 下棋的 矩阵位置
        let col = Math.ceil(x / gap_width) - 2;
        let row = Math.ceil(y / gap_width) - 2;
        if (arr2[row][col] === 5) {
            // 如果对应位置已有棋子
            arr2[row][col] = 6;
            draw(arr2);
        } else {
            // 如果对应位置已有棋子
            arr2[row][col] = 5;
            draw(arr2);
        }
    })

    const draw = arr2 => {
        for (let row = 0; row < arr2.length; row++) {
            for (let col = 0; col < arr2[row].length; col++) {
                // 计算棋子矩阵对应的 像素 位置
                let y = (row + 1) * gap_width;
                let x = (col + 1) * gap_width;
                // 绘制棋子
                if (arr2[row][col] === 6) {
                    ctx.fillStyle = "#000000";
                    ctx.fillRect(x - gap_width / 2, y - gap_width / 2, gap_width, gap_width);
                } else {
                    ctx.fillStyle = "#75988b";
                    ctx.fillRect(x - gap_width / 2, y - gap_width / 2, gap_width, gap_width);
                }
            }
        }
        draw_line();
    }


    const draw_attempt_path = () => {
        send(solution);
        let i = 0;
        let t = setInterval(() => {
            if (i === attempt_path.length) {
                clearInterval(t);
                return;
            }
            range_input.value = (i + 1) / attempt_path.length; // 刷新进度条
            number_input.value = i + 1; // 刷新步数
            if (i > 0) {
                let [x, y] = attempt_path[i - 1];
                ctx.clearRect((y + 0.5) * gap_width, (x + 0.5) * gap_width, gap_width, gap_width);
                ctx.fillStyle = "#75988b";
                ctx.fillRect((y + 0.5) * gap_width, (x + 0.5) * gap_width, gap_width, gap_width);// 绘制绿色底色
                ctx.fillStyle = "rgba(0,101,255,0.3)";
                ctx.fillRect((y + 0.5) * gap_width, (x + 0.5) * gap_width, gap_width, gap_width);
                ctx.fillStyle = "#000000";
                ctx.fillText(i.toString(), (y + 1) * gap_width, (x + 1) * gap_width);
            }
            let [x, y] = attempt_path[i];
            ctx.fillStyle = "#000000";
            ctx.fillText(i.toString(), (y + 1) * gap_width, (x + 1) * gap_width);
            ctx.drawImage(image, (y + 0.5) * gap_width, (x + 0.5) * gap_width, gap_width, gap_width);
            i++;
        }, 40)
    }
    const draw_target_path = () => {
        send(solution);
        let i = 0;
        let t = setInterval(() => {
            if (i === target_path.length) {
                clearInterval(t);
                return;
            }
            if (i > 0) {
                let [x, y] = target_path[i - 1];
                ctx.clearRect((y + 0.5) * gap_width, (x + 0.5) * gap_width, gap_width, gap_width);// 清空该区域
                ctx.fillStyle = "#75988b";
                ctx.fillRect((y + 0.5) * gap_width, (x + 0.5) * gap_width, gap_width, gap_width);// 绘制绿色底色
                ctx.fillStyle = "rgb(" + (40 + i).toString() + "," + (10 + i).toString() + ",20)";
                ctx.fillRect((y + 0.5) * gap_width, (x + 0.5) * gap_width, gap_width, gap_width);
                ctx.fillStyle = "white";
                ctx.fillText(i.toString(), (y + 1) * gap_width, (x + 1) * gap_width);
            }
            let [x, y] = target_path[i];
            ctx.fillStyle = "#000000";
            ctx.fillText(i.toString(), (y + 1) * gap_width, (x + 1) * gap_width);
            ctx.drawImage(image, (y + 0.5) * gap_width, (x + 0.5) * gap_width, gap_width, gap_width);
            i++;
        }, 40)
    }
    const draw_word = arr => {
        for (let i = 0; i < arr.length; i++) {
            let [row, col] = arr[i];
            let y = (row + 1) * gap_width;
            let x = (col + 1) * gap_width;
            ctx.fillStyle = "#000000";
            ctx.fillRect(x - gap_width / 2, y - gap_width / 2, gap_width, gap_width);
        }
    }
</script>

</html>